1 module hip.wasm; 2 version(WebAssembly): 3 import std.meta:AliasSeq; 4 5 ///WebAssembly.Table replacement for HipremeEngine 6 private __gshared ubyte* function(ubyte* args)[] _annonymousFunctionTable; 7 ///JSFunctions are represented opaquely right now. 8 alias JSFunction(T) = ubyte*; 9 10 11 ///Javascript function to call a D callback. 12 export extern(C) ubyte* __callDFunction(size_t addr, ubyte* args) 13 { 14 return _annonymousFunctionTable[addr](args); 15 } 16 17 ///Checks if function has been called with required arguments. 18 private ubyte* validateArguments(alias fn)(ubyte* args) 19 { 20 import std.traits; 21 //Only checking the count of 22 assert(Parameters!(fn).length <= *cast(size_t*)args, 23 fn.stringof~"Expected "~Parameters!(fn).length.stringof~" parameters"); 24 return args + size_t.sizeof; //Only uses 1 size_t to determine arguments validity 25 } 26 27 28 struct Arguments(alias Func) 29 { 30 import std.traits; 31 Parameters!Func params; 32 } 33 34 /** 35 * This is a special struct, loaded with the source pointer from bridge_malloc. 36 */ 37 struct WasmParametersMemory 38 { 39 ubyte* ptr; 40 } 41 42 Struct loadMemoryInStruct(Struct)(ubyte* arg, ubyte* rootArg) 43 { 44 import core.stdc.string:memcpy; 45 Struct ret; 46 size_t last = 0; 47 foreach(ref v; ret.tupleof) 48 { 49 static if(is(typeof(v) == string) || is(typeof(v) == ubyte[])) 50 { 51 { 52 ubyte* data = arg+last; 53 size_t length = *cast(size_t*)data; 54 v = cast(typeof(v))data[size_t.sizeof..length+size_t.sizeof]; 55 } 56 } 57 else static if(is(typeof(v) == WasmParametersMemory)) 58 v.ptr = rootArg; 59 else 60 memcpy(&v, arg+last, v.sizeof); 61 last+= v.sizeof; 62 } 63 return ret; 64 } 65 66 Arguments!fn wasmParametersFromUbyte(alias fn)(ubyte* arg) 67 { 68 return loadMemoryInStruct!(Arguments!fn)(arg, arg); 69 } 70 71 /** 72 * Whenever wanting to pass a callback to Javascript, call this function instead. 73 * This function is not expected to meet usercode. But it will stay here nevertheless. 74 */ 75 ubyte* sendJSFunction(alias fn)() 76 { 77 import std.traits; 78 static ubyte* function(ubyte* arg) convertedFunc = (ubyte* arg) 79 { 80 Arguments!fn params = wasmParametersFromUbyte!fn(validateArguments!fn(arg)); 81 static if(!is(ReturnType!fn == void)) 82 return fn(params.tupleof); 83 else 84 { 85 fn(params.tupleof); 86 return null; 87 } 88 }; 89 ///Gets a unique function index for usage in the table. Since function addresses aren't too big, we can use a simple array. 90 size_t addr = cast(size_t)(cast(ubyte*)fn); 91 if(addr >= _annonymousFunctionTable.length) _annonymousFunctionTable.length = addr+1; 92 _annonymousFunctionTable[addr] = convertedFunc; 93 94 return cast(ubyte*)fn; 95 } 96 97 98 struct JSDelegate 99 { 100 ubyte* funcHandle; 101 ubyte* funcptr; 102 ubyte* ctx; 103 } 104 105 alias JSStringType = AliasSeq!(size_t, void*); 106 107 struct JSString 108 { 109 size_t length; 110 void* ptr; 111 this(string str) 112 { 113 length = str.length; 114 ptr = cast(void*)str.ptr; 115 } 116 } 117 118 alias JSDelegateType(T) = AliasSeq!(ubyte*, ubyte*, ubyte*); 119 120 JSDelegate sendJSDelegate(alias dg)() 121 { 122 import std.traits; 123 auto convertedFunc = toFunc!dg; 124 size_t addr = cast(size_t)cast(ubyte*)convertedFunc; 125 if(addr >= _annonymousFunctionTable.length) _annonymousFunctionTable.length = addr+1; 126 _annonymousFunctionTable[addr] = convertedFunc; 127 128 129 return JSDelegate(cast(ubyte*)addr, cast(ubyte*)dg.funcptr, cast(ubyte*)dg.ptr); 130 } 131 132 133 134 /** 135 * Generates a delegate which adds the `this` context from the arguments. 136 * It also packs the arguments sent from Javascript and transform them to the 137 * data the D delegate expects. There is also a validation in the arguments received. 138 */ 139 ubyte* function(ubyte* args) toFunc(alias dg)() 140 { 141 import std.traits; 142 import hip.wasm; 143 alias Params = Parameters!dg; 144 alias DgArgs = Arguments!dg; 145 enum Length = DgArgs.tupleof.length; 146 alias Ret = ReturnType!dg; 147 148 static ubyte* function(ubyte* arg) ret = (ubyte* arg) 149 { 150 size_t argsCount = *cast(size_t*)arg; 151 assert(argsCount >= 2, "D delegates expects at least 2 arguments [Function Pointer, Function Context]"); 152 assert(argsCount - 2 <= Length, "Expected "~Length.stringof~" parameters."); 153 size_t[3] baseArgs = (cast(size_t*)arg)[0..3]; 154 155 static DgArgs delegateArguments; 156 static if(Length > 0) 157 delegateArguments = loadMemoryInStruct!DgArgs(arg + size_t.sizeof*3, arg); 158 159 static if(Length > 0) 160 { 161 static if(is(Ret == void)) 162 void delegate(Params) dg; 163 else 164 ubyte* delegate(Params) dg; 165 } 166 else 167 { 168 static if(is(Ret == void)) 169 void delegate() dg; 170 else 171 ubyte* delegate() dg; 172 } 173 174 dg.funcptr = cast(typeof(dg.funcptr))baseArgs[1]; 175 dg.ptr = cast(void*)baseArgs[2]; 176 177 static if(Length > 0) 178 { 179 static if(is(Ret == void)) 180 { 181 dg(delegateArguments.tupleof); 182 return null; 183 } else return dg(delegateArguments.tupleof); 184 } 185 else 186 { 187 static if(is(Ret == void)) 188 { 189 dg(); 190 return null; 191 } else return dg(); 192 } 193 }; 194 return ret; 195 } 196 197 /** 198 * The first index of a wasm array (which is a ptr) is a size_t value containing length 199 * Params: 200 * ptr = The ptr returnt from wasm 201 * Returns: Converted to an array 202 */ 203 ubyte[] getWasmArray(ubyte* ptr) 204 { 205 if(ptr is null) 206 return null; 207 size_t length = *cast(size_t*)ptr; 208 return (ptr+size_t.sizeof)[0..length]; 209 } 210 211 212 213 ubyte[] getWasmBinary(ubyte* input) 214 { 215 size_t length = *cast(size_t*)input; 216 ubyte[] ret = (input+size_t.sizeof)[0..length]; 217 return ret; 218 } 219 220 void freeWasmBinary(ubyte[] binary) 221 { 222 import core.memory; 223 ubyte* ptr = binary.ptr - size_t.sizeof; 224 GC.free(ptr); 225 }